<?php

/**
 * This file is part of the Codizer package.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright   Copyright (c) 2010 Advisa (http://www.advisa.fr)
 * @author      Codizer Core Team <team@codizer.org>
 * @package     Codizer
 */

class f_date_Date
{	
	private $dateString;
	private $year;
	private $month;
	private $day;
	private $hour;
	private $minutes;
	private $seconds;
	
	/**
	 * Get a new instance of f_date_Date
	 *
	 * @param string $dateString "Y-m-d H:i:s" or "Y-m-d"
	 * @return f_date_Date
	 */	
	private function __construct($dateString = null)
	{
		if(is_null($dateString))
		{
			$dateString = date('Y-m-d h:i:s');
		}
		else
		{
			if (!preg_match('#^(\\d{4})\-(\\d{1,2})\-(\\d{1,2})(\s+(\\d{1,2}):(\\d{1,2}):(\\d{1,2}))?$#', $dateString))
			{
				throw new Exception("Invalid date format : ".$dateString." format must be 'Y-m-d h:i:s' or 'Y-m-d'");
			}
        }
        $date = explode(" ", $dateString);

        list($this->year, $this->month, $this->day) = explode("-", $date[0]);
        list($this->hour, $this->minutes, $this->seconds) = explode(":", $date[1]);

        if(!checkdate($this->month, $this->day, $this->year))
        {
            throw new Exception("Invalid date : ".$dateString." date does not exist");
        }

        $this->dateString = $dateString;
	}
	
	/**
	 * Get a new instance of f_date_Date
	 *
	 * @param string $dateString "Y-m-d H:i:s" or "Y-m-d"
	 * @return f_date_Date
	 */
	public static function getInstance($dateString = null)
	{
		return new self($dateString);
	}
	
	/**
	 * Get the f_date_Date instance from an other date format
	 * ex : getInstanceFromFormat("15/08/1977", "d/m/Y");
	 *
	 * @param string $dateString
	 * @param string $format
	 * @return f_date_Date
	 */
	public static function getInstanceFromFormat($dateString, $format)
	{
		$dateTokens   = preg_split("/[\s.,:\/-]+/", $dateString);
		$formatTokens = preg_split("/[\s.,:\/-]+/", $format);

		$year   = date('Y');
		$month  = date('m');
		$day    = date('d');
		$hour   = date('00');
		$minute = date('00');
		$second = date('00');

		foreach ($formatTokens as $i => $token)
		{
			switch ($token)
			{
				case 'y' :
				case 'Y' :
					$year = str_pad($dateTokens[$i], 4, '0', STR_PAD_LEFT);
					break;
				case 'm' :
					$month = str_pad($dateTokens[$i], 2, '0', STR_PAD_LEFT);
					break;
                case 'M' :
                    $months = array(1 => 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec');
					$monthInDate = $dateTokens[$i];
                    $month = str_pad(array_search($monthInDate, $months),2, '0', STR_PAD_LEFT);
					break;
				case 'd' :
					$day = str_pad($dateTokens[$i], 2, '0', STR_PAD_LEFT);
					break;
				case 'h' :
				case 'H' :
					$hour = str_pad($dateTokens[$i], 2, '0', STR_PAD_LEFT);
					break;
				case 'i' :
					$minute = str_pad($dateTokens[$i], 2, '0', STR_PAD_LEFT);
					break;
				case 's' :
					$second = str_pad($dateTokens[$i], 2, '0', STR_PAD_LEFT);
					break;
			}
		}

		return self::getInstance("$year-$month-$day $hour:$minute:$second");
	}	
	
	/**
	 * Check if current date is before given date
	 *
	 * @param f_date_Date $date
	 * @return boolean
	 */
	public function isBefore($date)
	{
		if($date instanceof f_date_Date)
		{
			// compare by string, more secure
			$comp = strcmp($this->dateString, $date->dateString);
			if($comp < 0)
			{
				return true;
			}		
			return false;			
		}
		else 
		{
			throw new Exception("Date is not a valid object");	
		}
	}
	
	/**
	 * Check if date is after given date
	 *
	 * @param f_date_Date $date
	 * @return boolean
	 */
	public function isAfter(f_date_Date $date)
	{
		if($date instanceof f_date_Date)
		{
			$comp = strcmp($this->dateString, $date->dateString);
			if($comp > 0)
			{
				return true;
			}		
			return false;			
		}
		else 
		{
			throw new Exception("Date is not a valid object");	
		}
	}	
    
    /**
     * Check if date equals given date
     *
     * @param f_date_Date $date
     * @return boolean
     */
    public function equals($date)
    {
		if($date instanceof f_date_Date)
		{
			return $this->dateString == $date->dateString;		
		}
		else 
		{
			throw new Exception("Date is not a valid object");	
		}    	
    }

    public function diff($date = null)
    {
        if(is_null($date))
        {
            $date = f_date_Date::getInstance();
        }

        $time1 = $this->getTimeStamp();
        $time2 = $date->getTimeStamp();

        if ($time1 > $time2)
        {
          $ttime = $time1;
          $time1 = $time2;
          $time2 = $ttime;
        }

        $intervals = array('year','month','day','hour','minute','second');
        $diffs = array();

        foreach ($intervals as $interval)
        {
            $diffs[$interval] = 0;
            $ttime = strtotime("+1 " . $interval, $time1);
            while ($time2 >= $ttime)
            {
                $time1 = $ttime;
                $diffs[$interval]++;
                $ttime = strtotime("+1 " . $interval, $time1);
            }
        }

        return $diffs;
    }
	
    /**
     * Return the given date in a string format
     *
     * @return string
     */
	public function toString()
	{
		return $this->dateString;
	}


    public function getTimeStamp()
    {
        return strtotime($this->toString());
    }
    
//////////////////////////////// GENERIC METHODS //////////////////////////	
	
	public function getMonth()
	{
		return $this->month;
	}	
	
	public function getYear()
	{
		return $this->year;
	}	
	
	public function getDay()
	{
		return $this->day;
	}
	
	public function getHour()
	{
		return $this->hour;
	}	
	
	public function getMinutes()
	{
		return $this->minutes;
	}	
	
	public function getSeconds()
	{
		return $this->seconds;
	}	
	
	public function getDayOfWeek()
	{
		return date("w", strtotime($this->dateString));
	}	
	
	public function getDaysInMonth()
	{
		return date("t", $this->month);
	}
	
	public function getDayOfYear()
	{
		return date("z", strtotime($this->dateString));
	}
	
	public function isLeapYear()
	{
		return date("L", $this->year);
	}
}